#region Summary

// 
// Owner : JackieHan 
// 
// Solution : DailyCollection 
// 
// Project : Jackie.Utility.Notify.Services
// 
// File : ClientManager.cs
// 
// CopyRight : Copyright © 2010-2011 Skomart Corporation, All Rights Reserved 
// 
// Summary : 订阅消息客户端管理类
// 
// History : 
//			2011-04-28 17:17:03	    Create 
// 

#endregion

#region Using

using System;
using System.Collections.Generic;
using System.Timers;
using Jackie.Utility.CustomException;
using Jackie.Utility.CustomExtension;
using Jackie.Utility.Notify.Contracts;
using Jackie.Utility.Notify.Domain;

#endregion

namespace Jackie.Utility.Notify.Services.Manager
{
    /// <summary> 
    /// 订阅消息客户端管理类
    /// </summary>
    public class ClientManager
    {
        #region  Singleton

        private static ClientManager instance;
        private static readonly object LockObject = new object();

        public static ClientManager GetInstance()
        {
            if (instance == null)
            {
                lock (LockObject)
                {
                    if (instance == null)
                    {
                        instance = new ClientManager();
                        instance.KeepChannelAlive();
                    }
                }
            }
            return instance;
        }

        #endregion

        /// <summary>
        /// 所有客户端    key:clientSign
        /// </summary>
        private readonly IDictionary<string, ClientSubscribeInfo> clients;

        private ClientManager()
        {
            clients = new Dictionary<string, ClientSubscribeInfo>();
        }

        /// <summary>
        /// 订阅消息
        /// </summary>
        /// <param name="clientSign">客户端标识</param>
        /// <param name="titleCode">需要订阅的消息标题</param>
        /// <param name="callback">回调实现</param>
        public void SubscribeMessage(string clientSign, string titleCode, IMessageCallback callback)
        {
            var titleManager = TitleManager.GetInstance();
            var title = titleManager.GetTitle(titleCode);
            if (clients.ContainsKey(clientSign))   //该客户端已经订阅过消息
            {
                var existedClient = clients[clientSign];
                if (existedClient.SubscribedTitles.Count == 0) //该客户端现在没有已订阅的消息标题,先订阅保持连接消息
                {
                    var keepAliveTitle = titleManager.GetKeepAliveTitle();
                    existedClient.SubscribedTitles.Add(keepAliveTitle);
                }
                if (!existedClient.IsSubscribed(titleCode))  //判断是否是重复订阅
                {
                    existedClient.CallbackInstance = callback;
                    existedClient.SubscribedTitles.Add(title);
                }
                else  //如果订阅了就更新回调实现
                {
                    existedClient.CallbackInstance = callback;
                }
            }
            else   //新客户端，自动订阅保持连接的消息
            {
                var keepAliveTitle = titleManager.GetKeepAliveTitle();
                var newClient = new ClientSubscribeInfo() { CallbackInstance = callback, ClientSign = clientSign };
                var subscribedTitles = new List<TitleInfo>() { title, keepAliveTitle };
                newClient.SubscribedTitles = subscribedTitles;
                clients.Add(clientSign, newClient);
            }

            //如果消息不是即时消息，则需要发送历史消息
            if (!title.InstantMessage)
            {
                var messageManager = MessageManager.GetInstance();
                messageManager.SendNotSendMessage(title, clients[clientSign]);
            }

        }

        /// <summary>
        /// 取消订阅消息
        /// </summary>
        /// <param name="clientSign">客户端标识</param>
        /// <param name="titleCode">消息标题编码</param>
        public void UnsubscribeMessage(string clientSign, string titleCode)
        {
            if (!clients.ContainsKey(clientSign))
                throw new CodeNotExistedException("ClientSubscribeInfo", clientSign,
                                                  "the client never subscribe any title!");

            var client = clients[clientSign];

            if (!client.IsSubscribed(titleCode))
                throw new CodeNotExistedException("TitleInfo", titleCode,
                                                  string.Format(
                                                      "the client does not subscribe the title[code:{0}]!",
                                                      titleCode));
            client.RemoveSubscribedTitle(titleCode);
            if (client.SubscribedTitles.Count == 1)  //如果客户端订阅的消息标题只剩一个，则该标题一定是保持连接消息，也移除掉
            {
                var titleManager = TitleManager.GetInstance();
                client.RemoveSubscribedTitle(titleManager.GetKeepAliveTitle().Code);
            }
        }

        /// <summary>
        /// 获取订阅了消息标题的所有客户端
        /// </summary>
        /// <param name="titleCode">消息标题编码</param>
        /// <returns></returns>
        public IList<ClientSubscribeInfo> GetSubscribedClients(string titleCode)
        {
            var subscribedClients = new List<ClientSubscribeInfo>();
            clients.Values.ForEach(item =>
                                       {
                                           if (item.IsSubscribed(titleCode))
                                               subscribedClients.Add(item);
                                       });
            return subscribedClients;
        }

        /// <summary>
        /// 保持所有客户端通道是活动的
        /// </summary>
        /// <remarks>
        /// 每隔5分钟自动发送保持活动消息
        /// </remarks>
        public void KeepChannelAlive()
        {
            var keepAliveTimer = new Timer(300000);
            keepAliveTimer.Elapsed += new ElapsedEventHandler(KeepAliveTimer_Elapsed);
            keepAliveTimer.Start();
        }

        private void KeepAliveTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            var messageManager = MessageManager.GetInstance();
            messageManager.SendKeepAliveMessage();
        }
    }
}